/************************************************************************

								Heroes IV
					  Copyright 2002, The 3DO Company

	------------------------------------------------------------------
  						 combat_area_shader.cpp

	$Header:  $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "combat_area_shader.h"

#include <stdlib.h>

#include "battlefield.h"
#include "battlefield_metrics.h"
#include "battlefield_window.h"
#include "bitmap_layer.h"
#include "combat_area_shader_data.h"
#include "combat_object_type.h"
#include "combat_window.h"


// -----------------------------------------------------------------------
// Constructor.
// -----------------------------------------------------------------------
t_combat_area_shader::t_combat_area_shader( t_battlefield & battlefield )
	: t_abstract_combat_object( &battlefield)
{
	set_alpha( battlefield, 5 );
}

t_combat_area_shader::~t_combat_area_shader()
{
}

// -----------------------------------------------------------------------
// Shade a triangle part of a tile the give color in a raw bitmap
// -----------------------------------------------------------------------
static void shade_triangle( t_uint8 * raw_bitmap,
							int bitmap_width, int bitmap_height,
							t_uint8 color,
							t_screen_point left1, t_screen_point right1,
							t_screen_point left2, t_screen_point right2 )
{
	assert( left1.x == left2.x );
	assert( right1.x == right2.x );
	assert( left1.x < right1.x );

	int delta_y_1 = ( ( left1.y - right1.y ) << 16 ) / ( left1.x - right1.x );
	int delta_y_2 = ( ( left2.y - right2.y ) << 16 ) / ( left2.x - right2.x );

	int x, y1, y2;

	y1 = left1.y << 16;
	y2 = left2.y << 16;

	for ( x = left1.x; x <= right1.x; x++ )
	{
		if ( x >= 0 && x < bitmap_width )
		{
			int y;

			for ( y = y1 >> 16; y <= y2 >> 16; y++ )
			{
				if ( y >= 0 && y < bitmap_height )
				{
					raw_bitmap[ x + ( y * bitmap_width ) ] = color;
				}
			}
		}

		y1 += delta_y_1;
		y2 += delta_y_2;
	}
}

// -----------------------------------------------------------------------
// Set up shading
// -----------------------------------------------------------------------
void t_combat_area_shader::set_shading ( t_combat_area_shader_data const & data, 
										 t_battlefield const & battlefield )
{
	if (get_list_position()._Mynode() != NULL)
		battlefield.invalidate( *this );

	if ( m_bitmap.get() != NULL )
		m_bitmap.reset();

	if ( data.is_empty() )
		return;

	t_battlefield_window * battlefield_window = battlefield.get_combat_window()->get_battlefield_window();

	t_map_rect_2d const & map_bounding_rect = data.get_bounding_rect();
	t_isometric_map< t_uint8 > const & tile_data = data.get_tile_data();

	t_map_point_2d map_point;

	// Determine on-screen boundaries
	t_screen_rect screen_bounding_rect;

	if( battlefield.is_valid( map_bounding_rect.top_left ) )
	{
		screen_bounding_rect.top = battlefield.get_cell_vertex_screen_point( map_bounding_rect.top_left ).y;
		if ( screen_bounding_rect.top < 0 )
			screen_bounding_rect.top = 0;
	}
	else
	{
		screen_bounding_rect.top = 0;
	}

	if ( battlefield.is_valid( map_bounding_rect.bottom_right ) )
	{
		screen_bounding_rect.bottom = battlefield.get_cell_vertex_screen_point( map_bounding_rect.bottom_right + t_map_point_2d( 1, 1 ) ).y;
		if ( screen_bounding_rect.bottom >= battlefield_window->get_height() )
			screen_bounding_rect.bottom = battlefield_window->get_height() - 1;
	}
	else
	{
		screen_bounding_rect.bottom = battlefield_window->get_height() - 1;
	}

	if ( battlefield.is_valid( map_bounding_rect.bottom_left() ) )
	{
		screen_bounding_rect.left = battlefield.get_cell_vertex_screen_point( map_bounding_rect.bottom_left() + t_map_point_2d( 0, 1 ) ).x;
		if ( screen_bounding_rect.left < 0 )
			screen_bounding_rect.left = 0;
	}
	else
	{
		screen_bounding_rect.left = 0;
	}

	if ( battlefield.is_valid( map_bounding_rect.top_right() ) )
	{
		screen_bounding_rect.right = battlefield.get_cell_vertex_screen_point( map_bounding_rect.top_right() + t_map_point_2d( 1, 0 ) ).x;
		if ( screen_bounding_rect.right >= battlefield_window->get_width() )
			screen_bounding_rect.right = battlefield_window->get_width() - 1;
	}
	else
	{
		screen_bounding_rect.right = battlefield_window->get_width() - 1;
	}

	// Draw to "raw" memory bitmap
	t_owned_array< t_uint8 > raw_bitmap( new t_uint8[ screen_bounding_rect.height() * screen_bounding_rect.width() ] );

	memset( raw_bitmap.get(), 0, screen_bounding_rect.height() * screen_bounding_rect.width() );

	for ( map_point.row = map_bounding_rect.top(); map_point.row < map_bounding_rect.bottom(); map_point.row++ )
	{
		for ( map_point.column = map_bounding_rect.left(); map_point.column < map_bounding_rect.right(); map_point.column++ )
		{
			if ( battlefield.is_valid( map_point ) )
			{
				t_uint8 color = tile_data.get( map_point );
				
				if ( color != 0 )
				{
					t_screen_point top = battlefield.get_cell_vertex_screen_point( map_point );
					t_screen_point left = battlefield.get_cell_vertex_screen_point( map_point + t_map_point_2d( 1, 0 ) );
					t_screen_point right = battlefield.get_cell_vertex_screen_point( map_point + t_map_point_2d( 0, 1 ) );
					t_screen_point bottom = battlefield.get_cell_vertex_screen_point( map_point + t_map_point_2d( 1, 1 ) );
					
					top -= screen_bounding_rect.top_left();
					left -= screen_bounding_rect.top_left();
					right -= screen_bounding_rect.top_left();
					bottom -= screen_bounding_rect.top_left();

					assert( top.x == bottom.x );
					assert( left.x < top.x );
					assert( top.x < right.x );

					shade_triangle( raw_bitmap.get(), 
									screen_bounding_rect.width(), screen_bounding_rect.height(),
									color, left, top, left, bottom );
					shade_triangle( raw_bitmap.get(), 
									screen_bounding_rect.width(), screen_bounding_rect.height(),
									color, top, right, bottom, right );
				}
			}
		}
	}

	t_owned_array< t_bitmap_row > bitmap_rows( new t_bitmap_row[ screen_bounding_rect.height() ] );

	t_shared_array<t_uint8> bitmap_data( new t_uint8[ screen_bounding_rect.height() * screen_bounding_rect.width() ] );

	int y;
	int current_row = 0;
	int current_datum = 0;

	t_screen_rect new_screen_bounding_rect = screen_bounding_rect;

	for ( y = 0; y < screen_bounding_rect.height(); y++ )
	{
		t_bitmap_row & row = bitmap_rows[current_row];
		int raw_bitmap_row_offset = y * screen_bounding_rect.width();

		row.offset = current_datum;

		row.left = 0;
		row.right = screen_bounding_rect.width();
		
		while ( row.left < screen_bounding_rect.width() && raw_bitmap[ row.left + raw_bitmap_row_offset ] == 0 )
			row.left++;

		if ( row.left == screen_bounding_rect.width() )
		{
			// Skip empty rows at top
			if ( current_row == 0 )
			{
				new_screen_bounding_rect.top++;
				continue;
			}

		}
		else
		{
			while ( row.right > row.left && raw_bitmap[ row.right + raw_bitmap_row_offset - 1 ] == 0 )
				row.right--;

			// Copy data
			int row_length = row.right - row.left;

			assert( current_datum + row_length <= screen_bounding_rect.height() * screen_bounding_rect.width() );

			memcpy( &bitmap_data[ current_datum ], &raw_bitmap[ row.left + raw_bitmap_row_offset ], row_length );

			current_datum += row_length;
		}

		current_row++;
	}

	// Skip empty rows at bottom
	current_row--;
	
	while ( current_row >= 0 && bitmap_rows[current_row].get_width() == 0 )
	{
		current_row--;
		new_screen_bounding_rect.bottom--;
	}

	if ( current_row == -1 )
		return;

	// m_bitmap_offset = new_screen_bounding_rect.top_left();

	t_shared_array<t_uint8> alpha_mask;

	// Create bitmap from data
	//m_bitmap.reset( new t_paletted_16_layer( "", new_screen_bounding_rect - m_bitmap_offset, 1, data.get_palette(),
	//									     bitmap_rows.get(), bitmap_data, alpha_mask ) );
	// set_screen_position( m_bitmap_offset );
	
	m_bitmap.reset( new t_paletted_16_layer( "", new_screen_bounding_rect, 1, data.get_palette(),
										     bitmap_rows.get(), bitmap_data, alpha_mask ) );

	set_screen_position( t_screen_point( 0, 0 ) );
	
	if (get_list_position()._Mynode() != NULL)
		battlefield.invalidate( *this );
}


// -----------------------------------------------------------------------
// Reset to no shading
// -----------------------------------------------------------------------
void t_combat_area_shader::reset( t_battlefield const & battlefield )
{
	if (get_list_position()._Mynode() != NULL)
		battlefield.invalidate( *this );

	if ( m_bitmap.get() != NULL )
		m_bitmap.reset();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_combat_area_shader::blocks_movement() const
{
	return false;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_combat_area_shader::draw_shadow_to(
									t_uint32				current_time,
									t_screen_rect const &	source_rect,
									t_abstract_bitmap16 &	dest,
									t_screen_point const &	dest_point ) const
{
	// No shadow
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_combat_area_shader::draw_shadow_to(
									t_uint32				current_time,
									t_abstract_bitmap16 &	dest,
									t_screen_point const &	dest_point ) const
{
	// No shadow
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_combat_area_shader::draw_to(
									t_uint32				current_time,
									t_screen_rect const &	source_rect,
									t_abstract_bitmap16 &	dest,
									t_screen_point const &	dest_point ) const
{
	if ( m_bitmap.get() != NULL )
	{
		m_bitmap->draw_to( source_rect, dest, dest_point, get_alpha() );
	}
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_combat_area_shader::draw_to(
									t_uint32				current_time,
									t_abstract_bitmap16 &	dest,
									t_screen_point const &	dest_point ) const
{
	if ( m_bitmap.get() != NULL )
	{
		m_bitmap->draw_to( dest, dest_point, get_alpha() );
	}
}


// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int	t_combat_area_shader::get_depth() const
{
	// Draws over underlays, under everything else. Position doesn't matter
	return k_depth_shader;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int t_combat_area_shader::get_footprint_size() const
{
	return 0;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
int t_combat_area_shader::get_height() const
{
	return 0;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_combat_object_type t_combat_area_shader::get_object_type() const
{
	return k_combat_object_type_unsaved;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_combat_area_shader::get_rect() const
{
	if ( m_bitmap.get() == NULL )
	{
		return t_screen_rect( 0, 0, 0, 0 );
	}

	// return m_bitmap->get_rect() + m_bitmap_offset;
	return m_bitmap->get_rect();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_combat_area_shader::get_rect( t_uint32 current_time ) const
{
	return get_rect();
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_combat_area_shader::get_shadow_rect() const
{
	return t_screen_rect( 0, 0, 0, 0 );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
t_screen_rect t_combat_area_shader::get_shadow_rect( t_uint32 current_time ) const
{
	return t_screen_rect( 0, 0, 0, 0 );
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_combat_area_shader::hit_test( t_uint32				current_time,
									 t_screen_point const &	point ) const
{
	return false;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_combat_area_shader::is_animated() const
{
	return false;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_combat_area_shader::is_underlay() const
{
	return false;
}


// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_combat_area_shader::needs_redrawing( t_uint32	last_update_time,
											t_uint32	current_time ) const
{
	return false;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_combat_area_shader::on_placed()
{
	
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_combat_area_shader::on_removed()
{

}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
void t_combat_area_shader::place_during_read( t_battlefield& battlefield )
{

}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_combat_area_shader::read( t_combat_reader& reader )
{
	return true;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
bool t_combat_area_shader::write( t_combat_writer& writer ) const
{
	return true;
}
